
#include "packet-info.h"
#include "packet-pool.h"
#include <stdlib.h>
#include <iostream>
using namespace std;


PacketInfo::PacketInfo() {
    pkt_info_table_ = NULL;
}

PacketInfo::~PacketInfo() {
    packet_info_t* current;
    packet_info_t* temp;
    HASH_ITER(hh, pkt_info_table_, current, temp) {
    	HASH_DEL(pkt_info_table_, current);
    	free(current);
    }
}

/*
 * The middle parameter of HASH_ADD_PTR should be the name of the key field.
 */
void PacketInfo::add_pkt_info(int pktid, int nodeid, bool prob) {
    packet_info_t* newinfo = (packet_info_t*)malloc(sizeof(packet_info_t));
    memset(newinfo, 0, sizeof(packet_info_t));
    newinfo->key.pkt_id = pktid;
    newinfo->key.nodeid = nodeid;
    newinfo->prob = prob;
    // cout << "------before----------" << endl;
    // packet_info_t* current;
    // packet_info_t* temp;
    // HASH_ITER(hh, pkt_info_table_, current, temp) {
    // 	cout << current->key.pkt_id << " " << current->key.nodeid << endl;
    // }

    HASH_ADD(hh, pkt_info_table_, key, sizeof(info_key_t), newinfo);

    // cout << "------after----------" << endl;
    // packet_info_t* currenta;
    // packet_info_t* tempa;
    // HASH_ITER(hh, pkt_info_table_, currenta, tempa) {
    // 	cout << currenta->key.pkt_id << " " << currenta->key.nodeid << endl;
    // }
    
    // cout << pktid << " " << nodeid << endl;
    // find_pkt_info(pktid, nodeid);
}

/*
 * The middle parameter of HASH_FIND_PTR should be the pointer to info_key_t*.
 * Return value is NULL if the key doesn't exist.
 */
packet_info_t * PacketInfo::find_pkt_info(int pktid, int nodeid) {
    packet_info_t* result;
    packet_info_t info;
    memset(&info, 0, sizeof(packet_info_t));
    info.key.pkt_id = pktid;
    info.key.nodeid = nodeid;

    HASH_FIND(hh, pkt_info_table_, &info.key, sizeof(info_key_t), result);
    // if(result)
    // 	cout << result->key.pkt_id << " " << result->key.nodeid << endl;
    return result;
}


void PacketInfo::del_pkt_info(packet_info_t* delinfo) {
    HASH_DEL(pkt_info_table_, delinfo);
    free(delinfo);
}


/*
 * When update one entry, you should first delete the old one,
 * and then add a new entry with the new values.
 * Why? Because it's a hash table.
 */
///////////////////no update for Hash table??
void PacketInfo::up_pkt_info(packet_info_t* oldinfo, bool newprob) {
    HASH_DEL(pkt_info_table_, oldinfo);
    oldinfo->prob = newprob;
    HASH_ADD(hh, pkt_info_table_, key, sizeof(info_key_t), oldinfo);
}


// There should be some problems!!!
void PacketInfo::update_pkt_info(hdr_report_t report, int nodeid) {
    bool new_prob;

    // for
    int seq_num = report.last_pkt_ - BITMAP_SIZE;
    nsaddr_t src_ip = report.src_ip_;
    int pktid;
    u_int16_t seq_temp;
    nsaddr_t src_temp;
    // cout << bitset<8>(report.bit_map_) << endl;

    for (int i = 0; i <= BITMAP_SIZE; i++) {
	if(seq_num >= 1) {
	    pktid = 0;
	    src_temp = src_ip;
	    seq_temp = seq_num;
	    HASH_JEN_MIX(src_temp, seq_temp, pktid);
	    if(seq_num == report.last_pkt_)
		new_prob = true;
	    else
		new_prob = (bool)(report.bit_map_ & 0x80)/*1000 0000*/;

	    update_pkt_info(pktid, nodeid, new_prob);
	}
	  
	report.bit_map_ <<= 1;
	seq_num ++;
    }
}

/*
 * if p is a native packet, set previous hop's prob of p to true
 * if p is an encoded packet, all encoded packet info for previous 
 *    hop should be true
 * while this updating for packet info should not cause updating probguess,
 * probguess is only updated when receive a state report
 */
void PacketInfo::update_pkt_info(Packet * p, int prev_hop) {
    if(HDR_COPE(p)->encoded_num_ == 0) { //Native Packet
	update_pkt_info(HDR_CMN(p)->cope_pkt_id(), prev_hop, 1);
    } else { //Encoded Packet
	for(int i = 0; i < HDR_COPE(p)->encoded_num_; i ++) {
	    update_pkt_info(HDR_COPE(p)->xoreds_[i].pkt_id_, prev_hop, 1);
	}
    }
}

void PacketInfo::update_pkt_info(int pktid, int nodeid, bool prob) {
    packet_info_t* old_info = find_pkt_info(pktid, nodeid);
    
    if(old_info == NULL) 
	add_pkt_info(pktid, nodeid, prob);
    else
	up_pkt_info(old_info, prob);   
}

/* get probability of a specific neighbor and pkt_id */
bool PacketInfo::get_prob(int nb, int pkt_id) {

    // cout << "Finding: " << nb << " " << pkt_id << endl;

    // packet_info_t* current;
    // packet_info_t* temp;
    // HASH_ITER(hh, pkt_info_table_, current, temp) {
    // 	cout << current->key.pkt_id
    // 	     << " " << current->key.nodeid
    // 	     << " " << (int)current->prob << endl;
    // }
    
    packet_info_t * pkt_info = find_pkt_info(pkt_id, nb);

    if(pkt_info != NULL) {
	// cout << " 1" << endl;
	return pkt_info->prob;
    } else {
	// cout << " 0" << endl;
	return false;
    }
}


/* return the number of packets from start_seq to last_seq */
int PacketInfo::get_pkt_count(int nb, 
			      nsaddr_t src_ip, 
			      uint16_t start_seq_num, 
			      uint16_t last_seq_num) {
    u_int16_t seq_iter = start_seq_num;
    int pkt_id;
    int count = 0;
    u_int16_t index;
    nsaddr_t src;
    for( ; seq_iter <= last_seq_num; seq_iter++) {
	if(seq_iter < 1)
	    continue;
	
	pkt_id = 0;
	src = src_ip;
	index = seq_iter;
	HASH_JEN_MIX(src, index, pkt_id);
	if(get_prob(nb, pkt_id) == true){
	    count++;
	}
    }
    return count;
}
